/*
 ******************************************************************
 *                                                                *
 *                         FileIO Library                         *
 *                                                                *
 * Sub-Library: File Lister                                       *
 * Author: Arash Partow - 2003                                    *
 * URL: http://www.partow.net                                     *
 *                                                                *
 * Copyright notice:                                              *
 * Free use of the FileIO Library is permitted under the          *
 * guidelines and in accordance with the most current version of  *
 * the Common Public License.                                     *
 * http://www.opensource.org/licenses/cpl1.0.php                  *
 *                                                                *
 ******************************************************************
*/


#ifndef INCLUDE_FILEIO_FILE_LISTER_HPP
#define INCLUDE_FILEIO_FILE_LISTER_HPP

#include <string>
#include <list>

#include <boost/filesystem/operations.hpp>

namespace fileio
{

   template<typename OutputIterator>
   void file_lister(const std::string& directory, OutputIterator& out, const bool recurse = false)
   {
      std::list< std::string > directory_list;
      directory_list.push_back(directory);

      while(!directory_list.empty())
      {
         boost::filesystem::path path(directory_list.front(),boost::filesystem::native);

         if(!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path))
         {
            directory_list.pop_front();
            continue;
         }

         boost::filesystem::directory_iterator dir_end;

         for(boost::filesystem::directory_iterator it(path); it != dir_end; ++it)
         {
            if(!boost::filesystem::is_directory(*it))
            {
               out = it->string();
               ++out;
            }
            else if (recurse)
            {
               directory_list.push_back(it->string());
            }
         }
         directory_list.pop_front();
      }
   }

   template<typename Handler>
   void file_lister_with_handler(const std::string& directory, const Handler& handler, const bool recurse = false)
   {
      std::list< std::string > directory_list;
      directory_list.push_back(directory);

      while(!directory_list.empty())
      {
         boost::filesystem::path path(directory_list.front(),boost::filesystem::native);

         if(!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path))
         {
            directory_list.pop_front();
            continue;
         }

         boost::filesystem::directory_iterator dir_end;

         for(boost::filesystem::directory_iterator it(path); it != dir_end; ++it)
         {
            if(!boost::filesystem::is_directory(*it))
            {
               handler(it->string());
            }
            else if (recurse)
            {
               directory_list.push_back(it->string());
            }
         }
         directory_list.pop_front();
      }
   }

   template<typename MatchPredicate, typename OutputIterator>
   void file_finder(const std::string& initial_directory,
                    const MatchPredicate matcher,
                    OutputIterator out,
                    const bool recurse = false)
   {
      std::list< std::string > directory_list;
      directory_list.push_back(directory);

      while(!directory_list.empty())
      {
         boost::filesystem::path path(directory_list.front(),boost::filesystem::native);

         if(!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path))
         {
            directory_list.pop_front();
            continue;
         }

         boost::filesystem::directory_iterator dir_end;

         for(boost::filesystem::directory_iterator it(path); it != dir_end; ++it)
         {
            if(!boost::filesystem::is_directory(*it) && matcher(it->string()))
            {
               out = it->string();
               ++out;
            }
            else if (recurse)
            {
               directory_list.push_back(it->string());
            }
         }
         directory_list.pop_front();
      }
   }

} // namespace fileio

#endif
